<!DOCTYPE html>
<!--
Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/value/histogram.html">
<link rel="import" href="/tracing/value/legacy_unit_info.html">

<script>
'use strict';
tr.exportTo('tr.v', function() {
  class ChartJsonConverter {
    /**
     * Parses Values from |charts|, converts them to Histograms, and adds those
     * to |histograms|.
     *
     * @param {!Array.<!Object>} charts
     * @param {!tr.v.HistogramSet} histograms
     */
    static convertChartJson(charts, histograms) {
      const traceValues = charts.charts.trace;

      // The chromeperf dashboard requires some Diagnostics to be shared, even
      // if there is only a single Histogram in the HistogramSet.
      const diagnosticCaches = new Map();
      function addSharedDiagnostic(hist, name, diagnostic) {
        if (!diagnosticCaches.has(name)) {
          diagnosticCaches.set(name, new Map());
        }
        const cache = diagnosticCaches.get(name);
        const cacheKey = diagnostic instanceof tr.v.d.GenericSet ?
          [...diagnostic].sort().join(',') : diagnostic.minDate;
        if (!cache.has(cacheKey)) {
          cache.set(cacheKey, diagnostic);
          histograms.addSharedDiagnostic(diagnostic);
        }
        hist.diagnostics.set(name, cache.get(cacheKey));
      }

      for (const [name, pageValues] of Object.entries(charts.charts)) {
        if (name === 'trace') continue;

        const pageValuesCount = Object.keys(pageValues).length;
        for (const [storyName, value] of Object.entries(pageValues)) {
          if (pageValuesCount > 1 && storyName === 'summary') continue;

          const unitInfo = tr.v.LEGACY_UNIT_INFO.get(value.units) || {};
          const unitName = unitInfo.name || 'unitlessNumber';
          const conversionFactor = unitInfo.conversionFactor || 1;

          let improvementDirection = tr.b.ImprovementDirection.DONT_CARE;
          if (unitInfo.defaultImprovementDirection !== undefined) {
            improvementDirection = unitInfo.defaultImprovementDirection;
          }
          // Metrics have the final say.
          if (value.improvement_direction !== undefined) {
            improvementDirection =
              ChartJsonConverter.convertImprovementDirection(
                  value.improvement_direction);
          }
          const unitNameSuffix = tr.b.Unit.nameSuffixForImprovementDirection(
              improvementDirection);

          const hist = histograms.createHistogram(
              value.name || name,
              tr.b.Unit.byName[unitName + unitNameSuffix], [], {
                binBoundaries: tr.v.HistogramBinBoundaries.SINGULAR,
                description: value.description || '',
              });

          if (traceValues) {
            const traceValue = traceValues[storyName] || {};
            let traceUrl = traceValue.cloud_url;
            if (!traceUrl && traceValue.file_path) {
              traceUrl = 'file://' + traceValue.file_path;
            }
            if (traceUrl) {
              addSharedDiagnostic(hist, tr.v.d.RESERVED_NAMES.TRACE_URLS,
                  new tr.v.d.GenericSet([traceUrl]));
            }
          }

          if (pageValuesCount > 1) {
            addSharedDiagnostic(hist, tr.v.d.RESERVED_NAMES.STORIES,
                new tr.v.d.GenericSet([storyName]));
          }

          const storyTags = [];
          if (value.grouping_label) {
            storyTags.push(`grouping_label:${value.grouping_label}`);
          }
          if (value.story_tags) {
            storyTags.push(...value.story_tags);
          }
          if (storyTags.length) {
            addSharedDiagnostic(hist, tr.v.d.RESERVED_NAMES.STORY_TAGS,
                new tr.v.d.GenericSet(storyTags));
          }

          if (charts.benchmark_name) {
            addSharedDiagnostic(hist, tr.v.d.RESERVED_NAMES.BENCHMARKS,
                new tr.v.d.GenericSet([charts.benchmark_name]));
          }

          if (charts.label) {
            addSharedDiagnostic(hist, tr.v.d.RESERVED_NAMES.LABELS,
                new tr.v.d.GenericSet([charts.label]));
          }

          if (charts.benchmarkStartMs) {
            addSharedDiagnostic(hist, tr.v.d.RESERVED_NAMES.BENCHMARK_START,
                new tr.v.d.DateRange(charts.benchmarkStartMs));
          }

          if (value.type === 'histogram' || value.buckets) {
            for (const bucket of value.buckets) {
              // Take the center of the bin. This coarse granularity can amplify
              // noise when a measurement moves from one bin to the next.
              const sample = conversionFactor * (bucket.high + bucket.low) / 2;
              for (let i = 0; i < bucket.count; ++i) {
                hist.addSample(sample);
              }
            }
          } else if (value.type === 'list_of_scalar_values' || value.values) {
            // |value.values| is undefined if the list_of_scalar_values is
            // empty.
            if (value.values) {
              for (const sample of value.values) {
                hist.addSample(sample * conversionFactor);
              }
            }
          } else if (value.type === 'scalar' || value.value !== undefined) {
            hist.addSample(value.value * conversionFactor);
          }
        }
      }
    }

    static convertImprovementDirection(improvementDirection) {
      switch (improvementDirection) {
        case 'down': return tr.b.ImprovementDirection.SMALLER_IS_BETTER;
        case 'up': return tr.b.ImprovementDirection.BIGGER_IS_BETTER;
        default: return tr.b.ImprovementDirection.DONT_CARE;
      }
    }
  }

  return {
    ChartJsonConverter,
  };
});
</script>
